﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ChholCmdPort {
		public class ChholRegister {
		object dic_lock = new object();
		Dictionary<uint, List<Action<ChholCmdPort.Pkg>>> recv_actions = new();
		public readonly Action<ChholCmdPort.Pkg> sendPkg;

		public ChholRegister(Action<ChholCmdPort.Pkg> sendPkg) {
			this.sendPkg = sendPkg;
		}

		uint getHash(byte from_port, byte to_port, ushort remote_addr) {
			return remote_addr + ((uint)from_port << 16) + ((uint)to_port << 24);
		}
		public void registRecvAction(uint hash, Action<ChholCmdPort.Pkg> action) {
			lock (dic_lock) {
				if (!recv_actions.ContainsKey(hash)) {
					recv_actions[hash] = new List<Action<ChholCmdPort.Pkg>>();
				}
				recv_actions[hash].Add(action);
			}
		}
		public void unregistRecvAction(uint hash, Action<ChholCmdPort.Pkg> action) {
			lock (dic_lock) {
				if (recv_actions.ContainsKey(hash)) {
					recv_actions[hash].Remove(action);
					if (recv_actions[hash].Count == 0) {
						recv_actions.Remove(hash);
					}
				}
			}
		}



		public void recvPkg(Cc9d.Pkg pkg) {
			ChholCmdPort.Pkg p = new ChholCmdPort.Pkg(pkg);
			recvPkg(p);
		}
		public void recvPkg(ChholCmdPort.Pkg p) {
			uint[] hash = {
				getHash(p.From_port,    p.To_port,  p.Remote_addr),
				getHash(0xff,           p.To_port,  p.Remote_addr),
				getHash(p.From_port,    0xff,       p.Remote_addr),
				getHash(0xff,           0xff,       p.Remote_addr),
				getHash(p.From_port,    p.To_port,  0xffff),
				getHash(0xff,           p.To_port,  0xffff),
				getHash(p.From_port,    0xff,       0xffff),
				getHash(0xff,           0xff,       0xffff),
			};
			var actions = new List<Action<ChholCmdPort.Pkg>>(16);
			lock (dic_lock) {
				actions.AddRange(hash.Where(h => recv_actions.ContainsKey(h)).SelectMany(h => recv_actions[h]));
			}
			foreach (var action in actions) {
				try {
					action?.Invoke(p);
				} catch (Exception e) {
					Console.WriteLine($"Error in action for {p}: {e.Message}");
				}
			}
		}

		public ChholRegisterFlag regist(byte from_port, byte to_port, ushort remote_addr, Action<ChholCmdPort.Pkg> action) {
			return new ChholRegisterFlag(this, getHash(from_port,to_port,remote_addr), action);
		}

		/// <summary>
		/// 发送数据包并等待匹配的响应，适用于请求-响应通信模式。
		/// 注意：只有当收发端口号和地址都匹配时，才能使用这个方法。
		/// </summary>
		/// <param name="spkg">要发送的数据包</param>
		/// <param name="timeout">等待响应的超时时间（毫秒）</param>
		/// <param name="getPkgCB">用于验证接收到的数据包是否为预期响应的回调函数，
		///                       当返回 true 时表示找到了匹配的响应</param>
		/// <returns>匹配的响应数据包；如果超时未收到匹配响应则返回 null</returns>
		public ChholCmdPort.Pkg? access(ChholCmdPort.Pkg spkg, int timeout, Func<ChholCmdPort.Pkg, bool> getPkgCB) {
			using (AutoResetEvent handshake_recv_event = new AutoResetEvent(false)) {
				ChholCmdPort.Pkg? target_pkg = null;
				using (var flag = this.regist(spkg.To_port, spkg.From_port, spkg.Remote_addr, rpkg => {
					if (getPkgCB?.Invoke(rpkg) == true) {
						target_pkg = rpkg;
						handshake_recv_event?.Set();
					}
				})) {
					this.sendPkg(spkg);
					if (handshake_recv_event.WaitOne(timeout) == false) {
						return null;
					} else {
						return target_pkg;
					}
				}
			}
		}



	}
}
